module nade.environment;

import std.stdio;
import std.string;
import std.conv;


class Environment {
	class Entry {
		this(string s, Object v, Entry n = null)
		{
			symbol = s;
			value = v;
			next = n;
		}

		string		symbol;
		Object		value;
		Entry		next = null;
	}

	this()
	{
		_entries = null;
	}
	this(Entry entries)
	{
		_entries = entries;
	}
	this(Environment env)
	{
		_entries = env._entries;
	}
	this(Object[string] values, Environment env)
	{
		_entries = env._entries;
		foreach(symbol, value; values) add(symbol, value);
	}
	this(string symbol, Object value)
	{
		_entries = new Entry(symbol, value, null);
	}
	this(string symbol, Object value, Environment env)
	{
		_entries = new Entry(symbol, value, env._entries);
	}

	Entry find(string symbol)
	{
		for(auto entry = _entries;  entry !is null;  entry = entry.next)
		{
			if(entry.symbol == symbol)
			{
				return entry;
			}
		}

		return null;
	}
	Object lookup(string symbol)
	{
		auto entry = find(symbol);
		if(entry !is null)
			return entry.value;

		throw new Exception("Lookup failed for " ~ symbol ~ " in " ~ toString);
	}
	T get(T)(string symbol)
	{
		return cast(T)lookup(symbol);
	}

	void update(string symbol, Object value)
	{
		for(auto entry = _entries;  entry !is null;  entry = entry.next)
		{
			if(entry.symbol is symbol)
			{
				entry.value = value;
				return;
			}
		}

		assert(false);
	}

	void add(string symbol, Object value)
	{
		_entries = new Entry(symbol, value, _entries);
	}

	Environment opCat(Environment rhs)
	{
		if(_entries is null) return new Environment(rhs);

		auto r = new Entry(_entries.symbol, _entries.value);
		auto o = r;
		auto i = _entries.next;
		while(i !is null)
		{
			o = o.next = new Entry(i.symbol, i.value);
			i = i.next;
		}
		o.next = rhs._entries;
		return new Environment(r);
	}

	override string toString()
	{
		string result;
		for(auto entry = _entries;  entry !is null;  entry = entry.next)
		{
			result ~= format("%s:%s ", entry.symbol, to!(string)(entry.value));
		}

		return result;
	}

	private {
		Entry		_entries;
	}
}
